home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Java Developer's Companion
/
Java Developer's Companion.iso
/
Javacup
/
PR8ADPL7.TAR
/
productivity_tools
/
PR8ADPL7
/
JFSserver.java
< prev
next >
Encoding:
Amiga
Atari
Commodore
DOS
FM Towns/JPY
Macintosh
Macintosh JP
NeXTSTEP
RISC OS/Acorn
Shift JIS
UTF-8
Wrap
Java Source
|
1996-05-23
|
30.8 KB
|
1,329 lines
// JFSserver.java
// The server for the java file system.
// Some notes about permissions
// - to read a file r permissions for that file
// - to write a file w permissions for file
// - to create a file w permissions for directory
// - to delete a file w permissions for file
// - to chmod a file p permissions for file
import java.io.*;
import java.net.*;
import java.util.Vector;
import java.util.Date;
import java.util.Hashtable;
import java.util.Enumeration;
import JFSdirectory;
import CryptConnection;
import FileSystem;
public class JFSserver
{
static LineOutputStream log; // log file
static Vector cons = new Vector(); // running connections
static int cnum = 0; // connection ID count
static ServerProperty bsent, brecv, // changing server properties
tcon, treq, tdata,
tsucc, tfail;
// Properties stored by the server.
static Vector pcache = new Vector();
// main
// The server starts here
public static void main(String argv[])
{
// Parse command line args
if (argv.length < 1 || argv.length > 2) {
System.err.println("usage : java JFSserver <root> [log]");
System.exit(1);
}
String logname = "server_log";
if (argv.length == 2) {
logname = argv[1];
}
if (!logname.equals("-")) {
// open log file
try {
RandomAccessFile raf =
new RandomAccessFile(logname, "rw");
raf.seek(raf.length());
log = new LineOutputStream(
new FileOutputStream(raf.getFD()));
}
catch(IOException e) {
System.err.println("Failed to open log file");
System.exit(1);
}
}
else {
// Write to stdout
log = new LineOutputStream(System.out);
}
FileSystem.init(argv[0]);
loginfo("FileSystem started on "+argv[0]);
// Open main socket
ServerSocket ss = null;
try
ss = new ServerSocket(9888);
catch(IOException e) {
System.err.println("Failed to open socket : "+e.getMessage());
System.exit(1);
}
loginfo("JFSserver started");
// store changing properties
putprop(bsent = new ServerProperty("Bytes sent","root","0",false));
putprop(brecv = new ServerProperty("Bytes received","root","0",false));
putprop(tcon = new ServerProperty("Connections","root","0",false));
putprop(treq = new ServerProperty("Requests","root","0",false));
putprop(tdata = new ServerProperty("Data replys","root","0",false));
putprop(tsucc = new ServerProperty("Success replys","root","0",false));
putprop(tfail = new ServerProperty("Fail replys","root","0",false));
// store contant properties
String now = new Date(System.currentTimeMillis()).toString();
putprop(new ServerProperty("Boot time","root",now,false));
String jpr[] = {"java.version", "Java version",
"java.vendor", "Java vendor",
"java.class.version", "Java class version" };
for(int i=0; i<jpr.length; i+=2) {
String got = System.getProperty(jpr[i]);
if (got == null) got = "unknown";
putprop(new ServerProperty(jpr[i+1], "root", got, false));
}
// load external properties
loadcache();
// Start timeout reaper
new IdleReaper().start();
// Listen for connections, creating new threads to handle each one
while(true) {
Socket con = null;
try
con = ss.accept();
catch(IOException e)
continue;
loginfo("Got connection from " +
con.getInetAddress().getHostName());
new ServerClient(con).start();
tcon.setval(tcon.getval() + 1);
}
}
// getprop
// Look up a property in the cache
static synchronized ServerProperty getprop(String name, String user)
{
int idx = indexprop(name, user);
return idx < 0 ? null : (ServerProperty)pcache.elementAt(idx);
}
// indexprop
// Returns the index of a property in the cache, or -1
private static synchronized int indexprop(String name, String user)
{
for(int i=0; i<pcache.size(); i++) {
ServerProperty c = (ServerProperty)pcache.elementAt(i);
if (c.name.equals(name) && c.user.equals(user))
return i;
}
return -1;
}
// putprop
// Write a property into the cache, and if external flush the
// cache to /etc/properties
static synchronized void putprop(ServerProperty n)
{
int old = indexprop(n.name, n.user);
if (old < 0) pcache.addElement(n);
else pcache.setElementAt(n, old);
if (n.external) savecache();
}
// allprops
// Returns an array of all the properties in the cache
static synchronized ServerProperty []allprops()
{
ServerProperty a[] = new ServerProperty[pcache.size()];
for(int i=0; i<pcache.size(); i++)
a[i] = (ServerProperty)pcache.elementAt(i);
return a;
}
// delprop
// Delete the property matching the given name and user
static synchronized void delprop(String name, String user)
{
int idx = indexprop(name, user);
if (idx != -1) {
pcache.removeElementAt(idx);
savecache();
}
}
// loadcache
// Read all the properties from /etc/properties into the cache
static private void loadcache()
{
BufferInputStream buf = null;
try buf = new BufferInputStream(
FileSystem.getfile("/etc/properties", 0, -1, -1));
catch(BadPathException e) {
loginfo("Failed to read /etc/properties");
return;
}
try {
while(true)
pcache.addElement(new ServerProperty(buf.gets()));
}
catch(IOException e);
loginfo("Properties cache started");
}
// savecache
// Write the cache out to /etc/properties
static private synchronized void savecache()
{
BufferOutputStream buf = new BufferOutputStream();
for(int i=0; i<pcache.size(); i++) {
ServerProperty p = (ServerProperty)pcache.elementAt(i);
if (p.external)
buf.puts(p.output());
}
try FileSystem.putfile("/etc/properties", 0, buf.getarray(),
"root", "text/plain");
catch(BadPathException e)
System.err.println("Could not write to /etc/properties : " +
e.getMessage());
}
// loginfo
// Write some informative message to the log
static void loginfo(String s)
{
Date d = new Date(System.currentTimeMillis());
synchronized(log)
log.puts("-- ["+d+"] "+s);
}
}
// ServerClient
// A connection to a JFS client. The client sends requests to the server, in
// the form
//
// REQUEST = REQTYPE HEADER* '\n' byte*
//
// REQTYPE = 'Request:' { 'Auth' | 'Get' | 'Put' | 'Stat' | 'Passwd' |
// 'Chmod' | 'Delete' | 'Purge' | 'Rename' | 'Mkdir' | 'Uinfo' |
// 'Ginfo' | 'Copy' | 'Close' | 'Getprop' | 'Setprop' |
// 'Delprop' | 'Ulist' | 'Purge' | 'Chtype' } '\n'
//
// HEADER = HeaderName':' HeaderValue '\n'
//
// The server sends replies to the client, in the form
//
// REPLY = REPTYPE HEADER* '\n' byte*
//
// REPTYPE = 'Reply:' { 'Success' | 'Fail' | 'Data' } '\n'
//
// HEADER = HeaderName':' HeaderValue '\n'
//
class ServerClient extends Thread
{
String name; // name of this user, or null
String host; // host user is connecting from
CryptInputStream in; // connection from client
CryptOutputStream out; // connection to client
int cnum; // connection ID
int sent; // bytes sent on this connection
int recv; // bytes received on this connection
long last; // time of last activity
ServerClient(Socket s)
{
synchronized(JFSserver.cons)
JFSserver.cons.addElement(this);
cnum = JFSserver.cnum++;
try {
CryptConnection con = new CryptConnection(s.getInputStream(),
s.getOutputStream());
in = con.getInputStream();
out = con.getOutputStream();
}
catch(IOException e);
host = s.getInetAddress().getHostName();
last = System.currentTimeMillis();
}
// run
// Forever read requests from this client, service them and send
// back replys. A client must send an Auth request with a valid name
// and password before doing any other requests.
public void run()
{
while(true) {
Message msg = null;
try msg = new Message(in);
catch(IOException e) {
break;
}
String req = msg.find("Request");
if (req == null) {
failreply("No Request header");
continue;
}
// Find out what the request is, and handle it
logrequest(msg);
if (req.equals("Auth"))
auth(msg);
else if (req.equals("Close"))
break;
else {
if (name == null) {
failreply("Unauthenticated");
continue;
}
if (req.equals("Get"))
get(msg);
else if (req.equals("Put"))
put(msg);
else if (req.equals("Passwd"))
passwd(msg);
else if (req.equals("Stat"))
stat(msg);
else if (req.equals("Chmod"))
chmod(msg);
else if (req.equals("Delete"))
delete(msg);
else if (req.equals("Rename"))
rename(msg);
else if (req.equals("Mkdir"))
mkdir(msg);
else if (req.equals("Uinfo"))
uinfo(msg);
else if (req.equals("Ginfo"))
ginfo(msg);
else if (req.equals("Copy"))
copy(msg);
else if (req.equals("Getprop"))
getprop(msg);
else if (req.equals("Putprop"))
putprop(msg);
else if (req.equals("Delprop"))
delprop(msg);
else if (req.equals("Ulist"))
ulist(msg);
else if (req.equals("Purge"))
purge(msg);
else if (req.equals("Chtype"))
chtype(msg);
else
failreply("Unknown request");
}
}
try {
in.close();
out.close();
}
catch(IOException e);
JFSserver.loginfo("Closed connection to "+host);
synchronized(JFSserver.cons)
JFSserver.cons.removeElement(this);
}
// shutdown
// Disconnect this client
void shutdown()
{
try {
in.close();
out.close();
}
catch(IOException e);
}
// auth
// Process an Auth request. Valid headers are:
// 'Username:' Name
// 'Password:' Password
void auth(Message msg)
{
String authname = msg.find("Username");
String authpass = msg.find("Password");
if (authname == null) {
failreply("No Username given");
return;
}
if (authpass == null) {
failreply("No Password given");
return;
}
JFSuser u;
try u = FileSystem.getuser(authname);
catch(BadUserException e) {
failreply("User not found");
return;
}
if (!u.password.equals(authpass)) {
failreply("Password incorrect");
return;
}
name = authname;
successreply();
}
// get
// Process a Get request. Valid headers are:
// 'File:' dir
// 'Start:' start byte (optional)
// 'End:' end byte (only if Start was given)
void get(Message msg)
{
String file = msg.find("File");
String start = msg.find("Start");
String end = msg.find("End");
if (file == null) {
failreply("No File given");
return;
}
if (start != null && end == null) {
failreply("No End given");
return;
}
// does the file exist, and can this user access it?
JFSfile fileinfo;
try fileinfo = accessfile(file, "r");
catch(BadPathException e) {
failreply(e.getMessage());
return;
}
if (fileinfo == null) {
failreply("File not found");
return;
}
Message reply;
if (fileinfo.type.equals("jfs/directory")) {
// send directory to user
reply = new Message();
reply.add("Reply","Data");
JFSdirectory listdir;
try listdir = FileSystem.getdir(file);
catch(BadPathException e) {
failreply("Couldn't list directory");
return;
}
BufferOutputStream buf = new BufferOutputStream();
listdir.output(buf);
reply.setdata(buf.getarray());
}
else if (fileinfo.type.equals("jfs/special")) {
// This is a device file. Call the appropriate driver to
// read from it.
DeviceDriver drv = null;
try {
Class dc = Class.forName(fileinfo.name+"Device");
drv = (DeviceDriver)dc.newInstance();
}
catch(Exception e) {
failreply(e.getClass().getName()+" loading "+
"device driver");
return;
}
try reply = drv.read(msg, this);
catch(IOException e) {
failreply("Error reading from device driver : "+
e.getMessage());
return;
}
}
else {
// send file to user
reply = new Message();
reply.add("Reply","Data");
int ver = 0;
if (fileinfo.multiversion) {
String version = msg.find("Version");
if (version != null) {
// Use given version number
if ((ver = Integer.parseInt(version)) < 1) {
failreply("Invalid version number");
return;
}
}
else {
// Use latest version
for(int i=0; i<fileinfo.versions.size(); i++) {
JFSversion vi = (JFSversion)fileinfo.
versions.elementAt(i);
if (vi.number > ver)
ver = vi.number;
}
}
}
byte data[] = null;
int st = -1, en = -1;
if (start != null) {
st = Integer.parseInt(start);
en = Integer.parseInt(end);
if (st < 0 || en < 0 || en < st) {
failreply("Invalid file section");
return;
}
}
try data = FileSystem.getfile(file, ver, st, en);
catch(BadPathException e) {
failreply("Couldn't read file");
return;
}
reply.setdata(data);
}
reply.send(out);
logreply(reply);
}
// put
// Process a Put request. Valid headers are:
// 'File:' filename
// 'Version:' number (optional)
// 'Content-type:' mimetype
// 'Content-length:' size
// Followed by size bytes of data
void put(Message msg)
{
String file = msg.find("File");
String version = msg.find("Version");
String type = msg.find("Content-type");
if (file == null) {
failreply("No File given");
return;
}
if (type == null)
type = "application/octet-stream";
if (msg.getdata() == null) {
// If no data given, assume 0 bytes
msg.setdata(new byte[0]);
}
// Is the mime type given sensible?
MimeType mt = new MimeType(type);
if (!mt.exact() || (mt.maj.equals("jfs") && !name.equals("root"))) {
failreply("Invalid mime type");
return;
}
// Can this user write to the file (if it exists) or it's
// directory (if it doesn't) ?
JFSuser u = null;
try u = FileSystem.getuser(name);
catch(BadUserException e) {
failreply("You don't exist");
}
JFSfile fileinfo = null;
try {
fileinfo = FileSystem.statfile(file);
if (fileinfo.type.equals("jfs/directory")) {
failreply("Cannot overwrite directory");
return;
}
if (u.perms(fileinfo).indexOf('w') < 0) {
failreply("Permission denied for file");
return;
}
}
catch(BadPathException e);
if (fileinfo == null) {
try {
JFSfile parinfo =
FileSystem.statfile(FileSystem.parent(file));
if (u.perms(parinfo).indexOf('w') < 0) {
failreply("Permission denied for directory");
return;
}
}
catch(BadPathException e) {
failreply("Parent directory not found");
return;
}
}
if (fileinfo != null && fileinfo.type.equals("jfs/special")) {
// This is a device file. Call the appropriate device driver.
DeviceDriver drv = null;
try {
Class dc = Class.forName(fileinfo.name+"Device");
drv = (DeviceDriver)dc.newInstance();
}
catch(Exception e) {
failreply(e.getClass().getName()+" loading "+
"device driver");
return;
}
try drv.write(msg, this);
catch(IOException e) {
failreply("Error writing to device driver : "+
e.getMessage());
return;
}
successreply();
}
else {
// This is a normal file. Write into it
try {
int v = version==null ? 0 : Integer.parseInt(version);
FileSystem.putfile(file, v, msg.getdata(), u.name,
mt.toString());
}
catch(BadPathException e) {
failreply("Error saving file");
return;
}
successreply();
}
}
// passwd
// Process a Passwd request. Valid headers are:
// 'Old:' oldpass
// 'New:' newpass
void passwd(Message msg)
{
}
// stat
// Process a Stat request. Valid headers are:
// 'File:' filename
void stat(Message msg)
{
String file = msg.find("File");
if (file == null) {
failreply("No File given");
return;
}
try accessparent(file, 'r');
catch(BadPathException e) {
failreply(e.getMessage());
return;
}
// Does this file exist?
JFSfile fileinfo = null;
try fileinfo = FileSystem.statfile(file);
catch(BadPathException e) {
failreply("Couldn't stat file");
return;
}
JFSdirectory tmpdir = new JFSdirectory();
tmpdir.add(fileinfo);
BufferOutputStream buf = new BufferOutputStream();
tmpdir.output(buf);
Message reply = new Message();
reply.add("Reply","Data");
reply.setdata(buf.getarray());
reply.send(out);
logreply(reply);
}
// chmod
// Process a Chmod request. Valid headers are:
// 'File:' filename
// 'User:' user/groupname
// 'Perms:' permissions (optional)
void chmod(Message msg)
{
String file = msg.find("File");
String user = msg.find("User");
String perms = msg.find("Perms");
if (file == null) {
failreply("No File given");
return;
}
if (user == null) {
failreply("No User given");
return;
}
// Can this user change permissions on this file?
JFSfile fileinfo = null;
try fileinfo = accessfile(file, "p");
catch(BadPathException e) {
failreply(e.getMessage());
return;
}
if (fileinfo == null) {
failreply("File not found");
return;
}
// Change the permissions
try FileSystem.chmod(file, user, perms);
catch(BadPathException e) {
failreply(e.getMessage());
return;
}
successreply();
}
// delete
// Process a Delete request. Valid headers are:
// 'File:' filename
// 'Version:' number (optional)
void delete(Message msg)
{
String file = msg.find("File");
String version = msg.find("Version");
if (file == null) {
failreply("No File given");
return;
}
// can this user access the file?
try accessfile(file, "w");
catch(BadPathException e) {
failreply(e.getMessage());
return;
}
// Delete the file
int v = version == null ? 0 : Integer.parseInt(version);
try FileSystem.delete(file, v);
catch(BadPathException e) {
failreply("File not found");
return;
}
successreply();
}
// rename
// Process a Rename request. Valid headers are
// 'Source:' sourcefile
// 'Destination:' destfile
void rename(Message msg)
{
String src = msg.find("Source");
String dst = msg.find("Destination");
if (src == null) {
failreply("No Source given");
return;
}
if (dst == null) {
failreply("No Destination given");
return;
}
// Can we access the source file for reading & writing?
JFSfile srcinfo = null;
try srcinfo = accessfile(src, "rw");
catch(BadPathException e) {
failreply(e.getMessage()+" for source file");
return;
}
// if the dest file exists, can we access it? and if it doesn't can
// we access the dir it's in?
JFSfile dstinfo = null;
try dstinfo = accessfile(dst, "w");
catch(BadPathException e) {
failreply(e.getMessage()+" for destination file");
return;
}
if (dstinfo == null) {
try accessparent(dst, 'w');
catch(BadPathException e) {
failreply(e.getMessage()+" for destination dir");
return;
}
}
// do the renaming
try FileSystem.rename(src, dst, name);
catch(BadPathException e) {
failreply(e.getMessage());
return;
}
successreply();
}
// mkdir
// Process a Mkdir request. Valid headers are:
// 'Directory:' pathname
void mkdir(Message msg)
{
String dir = msg.find("Directory");
if (dir == null) {
failreply("No Directory given");
return;
}
// Can we access this dir's parent?
try accessparent(dir, 'w');
catch(BadPathException e) {
failreply(e.getMessage());
return;
}
// Create the directory
try FileSystem.mkdir(dir, name);
catch(BadPathException e) {
failreply("Failed to create directory");
return;
}
successreply();
}
// uinfo - Process a Uinfo request. Valid headers are:
// 'Username:' user
void uinfo(Message msg)
{
String user = msg.find("Username");
if (user == null) {
failreply("No Username given");
return;
}
// get this user's details
JFSuser u = null;
try u = FileSystem.getuser(user);
catch(BadUserException e) {
failreply("User not found");
return;
}
u.password = "";
// send details to client
String ustr = u.output();
byte ubuf[] = new byte[ustr.length()];
for(int i=0; i<ubuf.length; i++)
ubuf[i] = (byte)ustr.charAt(i);
Message r = new Message();
r.add("Reply","Data");
r.setdata(ubuf);
r.send(out);
logreply(r);
}
// ginfo
// Process a Ginfo request. Valid headers are
// 'Group:' name
void ginfo(Message msg)
{
String group = msg.find("Group");
if (group == null) {
failreply("No Group given");
return;
}
// get the list of users in this group
Vector gv = null;
try gv = FileSystem.getgroup(group);
catch(BadGroupException e) {
failreply("Error reading group");
return;
}
// send list to user
BufferOutputStream buf = new BufferOutputStream();
for(int i=0; i<gv.size(); i++)
buf.puts((String)gv.elementAt(i));
Message r = new Message();
r.add("Reply","Data");
r.setdata(buf.getarray());
r.send(out);
logreply(r);
}
// copy
// Process a Copy request. Valid headers are:
// 'Source:' sourcefile
// 'Destination:' destfile
void copy(Message msg)
{
String src = msg.find("Source");
String dst = msg.find("Destination");
if (src == null) {
failreply("No Source given");
return;
}
if (dst == null) {
failreply("No Destination given");
return;
}
// can we access the source file?
JFSfile srcinfo = null;
try srcinfo = accessfile(src, "r");
catch(BadPathException e) {
failreply(e.getMessage()+" for source file");
return;
}
// if the dest file exists, can we access it? and if it doesn't can
// we access the dir it's in?
JFSfile dstinfo = null;
try dstinfo = accessfile(dst, "w");
catch(BadPathException e) {
failreply(e.getMessage()+" for destination file");
return;
}
if (dstinfo == null) {
try accessparent(dst, 'w');
catch(BadPathException e) {
failreply(e.getMessage()+" for destination dir");
return;
}
}
// do the copy
try FileSystem.copy(src, dst, name);
catch(BadPathException e) {
failreply(e.getMessage());
return;
}
successreply();
}
// getprop
// Proces a Getprop request to get one property. Valid headers are:
// 'Property:' name
// 'User:' username
void getprop(Message msg)
{
String property = msg.find("Property");
String user = msg.find("User");
if (property == null) {
failreply("No Property given");
return;
}
if (user == null) {
failreply("No User given");
return;
}
ServerProperty p = JFSserver.getprop(property, name);
if (p == null) {
failreply("Property not found");
return;
}
Message r = new Message();
r.add("Reply","Data");
String pstr = p.output();
byte pb[] = new byte[pstr.length()];
pstr.getBytes(0, pstr.length(), pb, 0);
r.setdata(pb);
r.send(out);
logreply(r);
}
// putprop
// Handle a Putprop request. The property to store is passed as data
// in the message.
void putprop(Message msg)
{
ServerProperty p = null;
byte data[] = msg.getdata();
if (data == null) {
failreply("No data given??");
return;
}
try p = new ServerProperty(new String(msg.getdata(), 0));
catch(IOException e) {
failreply("Property format error");
return;
}
if (!p.name.equals(name) && !name.equals("root")) {
failreply("Permission denied");
return;
}
ServerProperty existing = JFSserver.getprop(p.name, p.user);
if (existing != null && !existing.external) {
failreply("Cannot overwrite internal property");
return;
}
JFSserver.putprop(p);
successreply();
}
// delprop
// Handler a Delprop request to delete a server property. Valid
// headers are:
// 'Property:' name
// 'User:' username
void delprop(Message msg)
{
String property = msg.find("Property");
String user = msg.find("User");
if (property == null) {
failreply("No Property given");
return;
}
if (user == null) {
failreply("No User given");
return;
}
if (!user.equals(name) && !name.equals("root")) {
failreply("Permission denied");
return;
}
ServerProperty old = JFSserver.getprop(property, user);
if (old != null && !old.external) {
failreply("Cannot delete internal properties");
return;
}
JFSserver.delprop(property, user);
successreply();
}
// ulist
// Handle a Ulist request to get all server users (minus passwords).
void ulist(Message msg)
{
// Get /etc/users
BufferInputStream ibuf = null;
try ibuf = new BufferInputStream(
FileSystem.getfile("/etc/users", 0, -1, -1));
catch(BadPathException e) {
failreply("Could not read /etc/users : "+e.getMessage());
return;
}
// Write to output buffer
BufferOutputStream obuf = new BufferOutputStream();
try while(true) {
JFSuser u = new JFSuser(ibuf.gets());
u.password = "";
obuf.puts(u.output());
}
catch(IOException e);
// Return to client
Message r = new Message();
r.add("Reply", "Data");
r.setdata(obuf.getarray());
r.send(out);
}
// purge
// Process a Purge request. Valid headers are:
// 'File:' filename
void purge(Message msg)
{
String file = msg.find("File");
if (file == null) {
failreply("No File given");
return;
}
// can this user access the file?
JFSfile info = null;
try info = accessfile(file, "w");
catch(BadPathException e) {
failreply(e.getMessage());
return;
}
if (info == null) {
failreply("File not found");
return;
}
if (!info.multiversion) {
failreply("Cannot purge single version files");
return;
}
// Delete all versions except for the highest
Vector vv = info.versions;
for(int i=0; i<vv.size()-1; i++) {
int vn = ((JFSversion)vv.elementAt(i)).number;
try FileSystem.delete(file, vn);
catch(BadPathException e) {
failreply("Could not delete version "+vn+" : "+
e.getMessage());
return;
}
}
successreply();
}
void chtype(Message msg)
{
String file = msg.find("File");
String type = msg.find("Type");
if (file == null) {
failreply("No File given");
return;
}
if (type == null) {
failreply("No Type given");
return;
}
MimeType mt = new MimeType(type);
if (!mt.exact() || (mt.maj.equals("jfs") && !name.equals("root"))) {
failreply("Invalid mime type");
return;
}
// Can we access this file?
JFSfile info = null;
try info = accessfile(file, "w");
catch(BadPathException e) {
failreply(e.getMessage());
return;
}
if (info == null) {
failreply("File not found");
return;
}
// Change the type
info.type = type;
try FileSystem.putinfo(file, info);
catch(BadPathException e) {
failreply("Error writing file info : "+e.getMessage());
return;
}
successreply();
}
// accessparent
// Can this user access the given file's parent
void accessparent(String file, char mode) throws BadPathException
{
String par = null;
try par = FileSystem.parent(file);
catch(BadPathException e)
throw new BadPathException("Bad filename");
if (!par.equals("")) {
JFSfile fileinfo = null;
try fileinfo = FileSystem.statfile(par);
catch(BadPathException e) {
throw new BadPathException("Couldn't stat parent");
}
JFSuser u = null;
try u = FileSystem.getuser(name);
catch(BadUserException e) {
throw new BadPathException("You don't exist");
}
if (u.perms(fileinfo).indexOf(mode) < 0)
throw new BadPathException("Permission denied");
}
}
// accessfile
// Checks if the given file can be accessed by the current user.
// If the file is not found, null is returned.
// If it is found and can be accessed ok, a pointer to a JFSfile
// object is returned.
// If the file exists but this user can't access it, an exception is
// thrown.
JFSfile accessfile(String file, String mode) throws BadPathException
{
JFSfile info = null;
try info = FileSystem.statfile(file);
catch(BadPathException e)
return null;
// Can this user read it?
JFSuser u = null;
try u = FileSystem.getuser(name);
catch(BadUserException e)
throw new BadPathException("You don't exist");
for(int i=0; i<mode.length(); i++)
if (u.perms(info).indexOf(mode.charAt(i)) < 0)
throw new BadPathException("Permission denied");
return info;
}
// failreply
// Send a failure reply
void failreply(String reason)
{
Message fail = new Message();
fail.add("Reply","Fail");
fail.add("Reason",reason);
fail.send(out);
logreply(fail);
}
// successreply
// Send a success reply
void successreply()
{
Message succ = new Message();
succ.add("Reply","Success");
succ.send(out);
logreply(succ);
}
// logrequest
// Log a received message
void logrequest(Message m)
{
last = System.currentTimeMillis();
Date d = new Date(last);
String line = "-> ["+d+"] "+name+" "+host+" "+m.find("Request");
for(int i=0; i<m.size(); i++)
if (!m.name(i).equals("Request"))
line += " \""+m.name(i)+"="+m.value(i)+"\"";
synchronized(JFSserver.log)
JFSserver.log.puts(line);
// Add message size
int len = messagesize(m);
recv += len;
JFSserver.brecv.setval(JFSserver.brecv.getval() + len);
// Add sent count
JFSserver.treq.setval(JFSserver.treq.getval() + 1);
}
// logreply
// Log a sent reply
void logreply(Message m)
{
last = System.currentTimeMillis();
Date d = new Date(last);
String line = "<- ["+d+"] "+name+" "+host+" "+m.find("Reply");
for(int i=0; i<m.size(); i++)
if (!m.name(i).equals("Reply"))
line += " \""+m.name(i)+"="+m.value(i)+"\"";
synchronized(JFSserver.log)
JFSserver.log.puts(line);
// Add message size
int len = messagesize(m);
sent += len;
JFSserver.bsent.setval(JFSserver.bsent.getval() + len);
// Add reply counts
String rp = m.find("Reply");
if (rp != null) {
if (rp.equals("Data"))
JFSserver.tdata.setval(JFSserver.tdata.getval() + 1);
else if (rp.equals("Success"))
JFSserver.tsucc.setval(JFSserver.tsucc.getval() + 1);
else
JFSserver.tfail.setval(JFSserver.tfail.getval() + 1);
}
}
// messagesize
// Compute the size of a message
int messagesize(Message m)
{
int sz = 0;
for(int i=0; i<m.size(); i++)
sz += (m.name(i).length() + m.value(i).length());
byte d[] = m.getdata();
if (d != null) sz += d.length;
return sz;
}
}
// ServerProperty
// A tagged string stored on the server. Each property is identified by
// a name and a user, and can only be accessed by that user (and root).
// External properties are used for storing long-term data, such as server
// configurations and user preferences. Internal properties are used for
// server status information, and are not saved.
class ServerProperty
{
String name;
String user;
String value;
boolean external;
// Create a new property, read from a string
ServerProperty(String line) throws IOException
{
StringSplitter tok = new StringSplitter(line, ':');
if (tok.countTokens() != 4)
throw new IOException("ServerProperty format error");
name = tok.nextToken();
user = tok.nextToken();
value = tok.nextToken();
external = tok.nextToken().equals("y");
}
ServerProperty(String n, String u, String v, boolean e)
{
name = n;
user = u;
value = v;
external = e;
}
// output
// Convert this property back to a string
String output()
{
StringJoiner j = new StringJoiner(':');
j.add(name); j.add(user); j.add(value); j.add(external?"y":"n");
return j.toString();
}
// hashCode
// For storing this property in the cache hashtable
public int hashCode()
{
return name.hashCode() + user.hashCode();
}
// getval, setval
// Convenience functions for getting and setting the property
// value as an integer.
int getval() { return Integer.parseInt(value); }
void setval(int i) { value = String.valueOf(i); }
}
// IdleReaper
// Cleans up client connections that have been idle for longer than the
// time specified in the 'Client Timeout' property.
class IdleReaper extends Thread
{
// run
// Loop forever, waking up once every 60 seconds to check on clients.
public void run()
{
while(true) {
// Get current time and timeout
long now = System.currentTimeMillis();
ServerProperty timeoutp =
JFSserver.getprop("Client Timeout", "root");
if (timeoutp != null) {
long timeout = Integer.parseInt(timeoutp.value)*1000;
// Check clients
synchronized(JFSserver.cons) {
for(int i=0; i<JFSserver.cons.size(); i++) {
ServerClient c =
(ServerClient)JFSserver.cons.
elementAt(i);
if (now - c.last > timeout) {
// Die!
c.shutdown();
}
}
}
// Wait for 60 seconds
try Thread.sleep(60*1000);
catch(InterruptedException e);
}
}
}
}